﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace com.tdb.foundation
{
    /// <summary>
    ///     Extensions for strings
    /// </summary>
    public static class StringExtensions
    {
        #region IsDate

        /// <summary>
        ///     Returns true if value is a date
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsDate(this string value)
        {
            try
            {
                DateTime tempDate;
                return DateTime.TryParse(value, out tempDate);
            }

            catch (Exception)
            {
                return false;
            }
        }

        // IsDate

        #endregion

        #region IsInt

        /// <summary>
        ///     Returns true if value is an int
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsInt(this string value)
        {
            try
            {
                int tempInt;
                return int.TryParse(value, out tempInt);
            }

            catch (Exception)
            {
                return false;
            }
        }

        // IsInt

        #endregion

        #region Take

        /// <summary>
        ///     Like LINQ take - takes the first x characters
        /// </summary>
        /// <param name="value">the string</param>
        /// <param name="count">number of characters to take</param>
        /// <param name="ellipsis">true to add ellipsis (...) at the end of the string</param>
        /// <returns></returns>
        public static string Take(this string value, int count, bool ellipsis = false)
        {
            // get number of characters we can actually take
            var lengthToTake = Math.Min(count, value.Length);

            // Take and return
            return ellipsis && lengthToTake < value.Length
                ? string.Format("{0}...", value.Substring(0, lengthToTake))
                : value.Substring(0, lengthToTake);
        }

        // Take

        #endregion

        #region Skip

        /// <summary>
        ///     like LINQ skip - skips the first x characters and returns the remaining string
        /// </summary>
        /// <param name="value">the string</param>
        /// <param name="count">number of characters to skip</param>
        /// <returns></returns>
        public static string Skip(this string value, int count)
        {
            return value.Substring(Math.Min(count, value.Length) - 1);
        }

        // Skip

        #endregion

        #region IsNullOrEmpty

        /// <summary>
        ///     Null or empty check as extension
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty(this string value)
        {
            return string.IsNullOrEmpty(value);
        }

        // IsNullOrEmpty

        #endregion

        #region IsNOTNullOrEmpty

        /// <summary>
        ///     Returns true if the string is Not null or empty
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsNOTNullOrEmpty(this string value)
        {
            return !string.IsNullOrEmpty(value);
        }

        // IsNOTNullOrEmpty

        #endregion

        #region Formatted

        /// <summary>
        ///     "a string {0}".Formatted("blah") vs string.Format("a string {0}", "blah")
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public static string Formatted(this string format, params object[] args)
        {
            return string.Format(format, args);
        }

        // Formatted

        #endregion

        #region Match

        /// <summary>
        ///     Returns true if the pattern matches
        /// </summary>
        /// <param name="value"></param>
        /// <param name="pattern"></param>
        /// <returns></returns>
        public static bool Match(this string value, string pattern)
        {
            return Regex.IsMatch(value, pattern);
        }

        // Match

        #endregion

        #region ToEmptyString

        /// <summary>
        ///     Converts a null or "" to string.empty. Useful for XML code. Does nothing if <paramref name="value" /> already has a
        ///     value
        /// </summary>
        /// <param name="value">string to convert</param>
        public static string ToEmptyString(string value)
        {
            return string.IsNullOrEmpty(value) ? string.Empty : value;
        }

        // ToEmptyString

        #endregion

        #region ToStringPretty

        /*
        * Converting a sequence to a nicely-formatted string is a bit of a pain. 
        * The String.Join method definitely helps, but unfortunately it accepts an 
        * array of strings, so it does not compose with LINQ very nicely.
        * 
        * My library includes several overloads of the ToStringPretty operator that 
        * hides the uninteresting code. Here is an example of use:
        * 
        * Console.WriteLine(Enumerable.Range(0, 10).ToStringPretty("From 0 to 9: [", ",", "]"));
        * 
        * The output of this program is:
        * 
        * From 0 to 9: [0,1,2,3,4,5,6,7,8,9]
        */

        /// <summary>
        ///     Returns a comma delimited string
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        public static string ToStringPretty<T>(this IEnumerable<T> source)
        {
            return source == null ? string.Empty : ToStringPretty(source, ",");
        }

        /// <summary>
        ///     Returns a single string, delimited with <paramref name="delimiter" /> from source
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="delimiter"></param>
        /// <returns></returns>
        public static string ToStringPretty<T>(this IEnumerable<T> source, string delimiter)
        {
            return source == null ? string.Empty : ToStringPretty(source, string.Empty, delimiter, string.Empty);
        }

        /// <summary>
        ///     Returns a delimited string, appending <paramref name="before" /> at the start,
        ///     and <paramref name="after" /> at the end of the string
        ///     Ex: Enumerable.Range(0, 10).ToStringPretty("From 0 to 9: [", ",", "]")
        ///     returns: From 0 to 9: [0,1,2,3,4,5,6,7,8,9]
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="before"></param>
        /// <param name="delimiter"></param>
        /// <param name="after"></param>
        /// <returns></returns>
        public static string ToStringPretty<T>(this IEnumerable<T> source, string before, string delimiter,
            string after)
        {
            if (source == null)
                return string.Empty;

            var result = new StringBuilder();
            result.Append(before);

            var firstElement = true;
            foreach (var elem in source)
            {
                if (firstElement) firstElement = false;
                else result.Append(delimiter);

                result.Append(elem);
            }

            result.Append(after);
            return result.ToString();
        }

        // ToStringPretty

        #endregion
    }
}